﻿using ApplicationInfrastructure;
using System;
using System.Text;
using System.Threading;
using TI_BLE_HCI_ClientLib.DataEntities.Advertising;
using TI_BLE_HCI_ClientLib.HCIBaseEntities;
using TI_BLE_HCI_ClientLib.HCICommands;
using TI_BLE_HCI_ClientLib.HCIEvents;
using Utilities;

namespace TI_BLE_HCI_ClientLib.Controllers
{
    public class HCISimpleSerialPeripheralProfileController : HCIController
    {
        protected const int COMMAND_TIMEOUT_MSECS = 5000;
        protected const ushort INVALID_CONNECTION_HANDLE = 0xffff;
        protected const byte INVALID_BYTE_VALUE = 0xff;
        protected const ushort INVALID_USHORT_VALUE = 0xffff;

        public HCISimpleSerialPeripheralProfileController(string ComPort, int BaudRate)
            : base(DependencyResolver.GetFrameManager(DependencyResolver.GetSerialPortFullDuplexChannel(ComPort, BaudRate)))
        {
            base.HCIEventReceived += HCISimpleSerialController_HCIEventReceived;
        }

        void HCISimpleSerialController_HCIEventReceived(HCIEvent hciEvent)
        {
            if (hciEvent is HCIEvent_GAP_Link_Established)
            {
                if (LinkEstablished != null) LinkEstablished(hciEvent as HCIEvent_GAP_Link_Established);
                return;
            }

            if (hciEvent is HCIEvent_GAP_Link_Terminated)
            {
                if (LinkTerminated != null) LinkTerminated(hciEvent as HCIEvent_GAP_Link_Terminated);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Read_By_Group_Type_Request)
            {
                if (ServiceDiscoveryRequestReceived != null) ServiceDiscoveryRequestReceived(hciEvent as HCIEvent_ATT_Read_By_Group_Type_Request);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Find_By_Type_Value_Request)
            {
                if (FindServiceAttributesByTypeRequestReceived != null) FindServiceAttributesByTypeRequestReceived(hciEvent as HCIEvent_ATT_Find_By_Type_Value_Request);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Read_By_Type_Request)
            {
                if (ReadServiceAttributesByTypeRequestReceived != null) ReadServiceAttributesByTypeRequestReceived(hciEvent as HCIEvent_ATT_Read_By_Type_Request);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Read_Characteristic_Value_Request)
            {
                if (ReadCharacteristicValueRequestReceived != null) ReadCharacteristicValueRequestReceived(hciEvent as HCIEvent_ATT_Read_Characteristic_Value_Request);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Write_Characteristic_Value_Request)
            {
                if (WriteCharacteristicValueRequestReceived != null) WriteCharacteristicValueRequestReceived(hciEvent as HCIEvent_ATT_Write_Characteristic_Value_Request);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Prepare_Write_Characteristic_Value_Request)
            {
                if (PrepareWriteCharacteristicValueRequestReceived != null) PrepareWriteCharacteristicValueRequestReceived(hciEvent as HCIEvent_ATT_Prepare_Write_Characteristic_Value_Request);
                return;
            }

            if (hciEvent is HCIEvent_GAP_End_Discoverable)
            {
                if (DiscoverableEnd != null) DiscoverableEnd(hciEvent as HCIEvent_GAP_End_Discoverable);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Read_Characteristic_Blob_Value_Request)
            {
                if (ReadCharacteristicBlobValueRequestReceived != null) ReadCharacteristicBlobValueRequestReceived(hciEvent as HCIEvent_ATT_Read_Characteristic_Blob_Value_Request);
                return;
            }

            if (hciEvent is HCIEvent_ATT_Exchange_MTU_Request)
            {
                if (ExchangeMTURequestReceived != null) ExchangeMTURequestReceived(hciEvent as HCIEvent_ATT_Exchange_MTU_Request);
                return;
            }
        }

        public delegate void LinkEstablishedHandler(HCIEvent_GAP_Link_Established evt);
        public delegate void LinkTerminatedHandler(HCIEvent_GAP_Link_Terminated evt);
        public delegate void ServiceDiscoveryRequestReceivedHandler(HCIEvent_ATT_Read_By_Group_Type_Request evt);
        public delegate void FindServiceAttributesByTypeRequestReceivedHandler(HCIEvent_ATT_Find_By_Type_Value_Request evt);
        public delegate void ReadServiceAttributesByTypeRequestReceivedHandler(HCIEvent_ATT_Read_By_Type_Request evt);
        public delegate void ReadCharacteristicValueRequestReceivedHandler(HCIEvent_ATT_Read_Characteristic_Value_Request evt);
        public delegate void WriteCharacteristicValueRequestReceivedHandler(HCIEvent_ATT_Write_Characteristic_Value_Request evt);
        public delegate void PrepareWriteCharacteristicValueRequestReceivedHandler(HCIEvent_ATT_Prepare_Write_Characteristic_Value_Request evt);
        public delegate void ReadCharacteristicBlobValueRequestReceivedHandler(HCIEvent_ATT_Read_Characteristic_Blob_Value_Request evt);
        public delegate void DiscoverableEndHandler(HCIEvent_GAP_End_Discoverable evt);
        public delegate void ExchangeMTURequestReceivedHandler(HCIEvent_ATT_Exchange_MTU_Request evt);

        public event LinkEstablishedHandler LinkEstablished;
        public event LinkTerminatedHandler LinkTerminated;
        public event ServiceDiscoveryRequestReceivedHandler ServiceDiscoveryRequestReceived;
        public event FindServiceAttributesByTypeRequestReceivedHandler FindServiceAttributesByTypeRequestReceived;
        public event ReadServiceAttributesByTypeRequestReceivedHandler ReadServiceAttributesByTypeRequestReceived;
        public event ReadCharacteristicValueRequestReceivedHandler ReadCharacteristicValueRequestReceived;
        public event WriteCharacteristicValueRequestReceivedHandler WriteCharacteristicValueRequestReceived;
        public event PrepareWriteCharacteristicValueRequestReceivedHandler PrepareWriteCharacteristicValueRequestReceived;
        public event ReadCharacteristicBlobValueRequestReceivedHandler ReadCharacteristicBlobValueRequestReceived;
        public event DiscoverableEndHandler DiscoverableEnd;
        public event ExchangeMTURequestReceivedHandler ExchangeMTURequestReceived;

        enum StateMachineStateEnum
        {
            Idle,
            Phase1,
            Phase2,
            Phase3
        }

        public bool Init()
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Device_Init_Done evtDeviceInitDone = null;

            //this.Start();

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            object locker = new object();

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                lock (locker)
                {

                    if (evt is HCIEvent_GAP_Command_Status)
                    {
                        if (state == StateMachineStateEnum.Idle)
                        {
                            if ((evt as HCIEvent_GAP_Command_Status).Status == HCIVendorSpecificEventHostErrorCode.Success)
                            {
                                state = StateMachineStateEnum.Phase1;
                            }
                            else
                            {
                                eventWaitable.Set();
                            }

                            return;
                        }
                    }

                    if (evt is HCIEvent_GAP_Device_Init_Done)
                    {
                        if (state == StateMachineStateEnum.Phase1)
                        {
                            commandWaitable.Reset();
                            evtDeviceInitDone = evt as HCIEvent_GAP_Device_Init_Done;
                            eventWaitable.Set();
                        }

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_DEVICE_INIT
                this.SendCommand(
                    new HCICommand_GAP_Device_Init(
                            HCICommand_GAP_Device_Init.ProfileRoleEnum.GAPProfilePeripheral,
                            5,
                            new byte[16],
                            new byte[16],
                            1)
                        );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtDeviceInitDone != null && evtDeviceInitDone.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if !(MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public void Terminate()
        {
            this.Stop();

            base.HCIEventReceived -= HCISimpleSerialController_HCIEventReceived;
        }

        public bool SendErrorResponse(ushort connHandle, HCICommand_GATT_Error_Response.RequestOpCodeEnum reqOpCode, ushort attributeHandle, HCICommand_GATT_Error_Response.ErrorCodeEnum errCode)
        {
            try
            {
                // INVIO COMANDO GATT_ERROR_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Error_Response(connHandle, reqOpCode, attributeHandle, errCode)
                    );

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool UpdateAdvertisingData(HCICommand_GAP_Update_Advertising_Data.AdvertisingTypeEnum advertisingType, AdvertisingAndScanResponseData advertData)
        {
            return UpdateAdvertisingData(advertisingType, advertData.ToByteArray());
        }

        public bool UpdateAdvertisingData(HCICommand_GAP_Update_Advertising_Data.AdvertisingTypeEnum advertisingType, byte[] advertData)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Advert_Data_Update_Done evtAdvertDataUpdateDone = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            object locker = new object();

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                lock (locker)
                {
                    if (evt is HCIEvent_GAP_Command_Status)
                    {
                        if (state == StateMachineStateEnum.Idle)
                        {
                            if ((evt as HCIEvent_GAP_Command_Status).Status == HCIVendorSpecificEventHostErrorCode.Success)
                            {
                                state = StateMachineStateEnum.Phase1;
                            }
                            else
                            {
                                eventWaitable.Set();
                            }

                            return;
                        }
                    }

                    if (evt is HCIEvent_GAP_Advert_Data_Update_Done)
                    {
                        if (state == StateMachineStateEnum.Phase1)
                        {
                            var evtReceivedAdvertDataUpdateDone = evt as HCIEvent_GAP_Advert_Data_Update_Done;

                            commandWaitable.Reset();
                            evtAdvertDataUpdateDone = evtReceivedAdvertDataUpdateDone;
                            eventWaitable.Set();
                        }

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_UPDATE_ADVERTISING_DATA
                this.SendCommand(
                    new HCICommand_GAP_Update_Advertising_Data(advertisingType, advertData)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtAdvertDataUpdateDone != null && evtAdvertDataUpdateDone.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool MakeDiscoverable(TI_BLE_HCI_ClientLib.HCICommands.HCICommand_GAP_Make_Discoverable.EventTypeEnum eventType,
            TI_BLE_HCI_ClientLib.HCICommands.HCICommand_GAP_Make_Discoverable.InitiatorAddrTypeEnum initiatorAddrType,
            byte[] initiatorAddr, TI_BLE_HCI_ClientLib.HCICommands.HCICommand_GAP_Make_Discoverable.ChannelMapEnum channelMap,
            TI_BLE_HCI_ClientLib.HCICommands.HCICommand_GAP_Make_Discoverable.FilterPolicyEnum filterPolicy)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Make_Discoverable_Done evtMakeDiscoverableDone = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            object locker = new object();

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                lock (locker)
                {
                    if (evt is HCIEvent_GAP_Command_Status)
                    {
                        if (state == StateMachineStateEnum.Idle)
                        {
                            if ((evt as HCIEvent_GAP_Command_Status).Status == HCIVendorSpecificEventHostErrorCode.Success)
                            {
                                state = StateMachineStateEnum.Phase1;
                            }
                            else
                            {
                                eventWaitable.Set();
                            }

                            return;
                        }
                    }

                    if (evt is HCIEvent_GAP_Make_Discoverable_Done)
                    {
                        if (state == StateMachineStateEnum.Phase1)
                        {
                            var evtReceivedMakeDiscoverableDone = evt as HCIEvent_GAP_Make_Discoverable_Done;

                            commandWaitable.Reset();
                            evtMakeDiscoverableDone = evtReceivedMakeDiscoverableDone;
                            eventWaitable.Set();
                        }

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_MAKE_DISCOVERABLE
                this.SendCommand(
                    new HCICommand_GAP_Make_Discoverable(eventType, initiatorAddrType, initiatorAddr, channelMap, filterPolicy)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtMakeDiscoverableDone!=null && evtMakeDiscoverableDone.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool EndDiscoverable()
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_End_Discoverable evtEndDiscoverable = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            object locker = new object();

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                lock (locker)
                {
                    if (evt is HCIEvent_GAP_Command_Status)
                    {
                        if (state == StateMachineStateEnum.Idle)
                        {
                            if ((evt as HCIEvent_GAP_Command_Status).Status == HCIVendorSpecificEventHostErrorCode.Success)
                            {
                                state = StateMachineStateEnum.Phase1;
                            }
                            else
                            {
                                eventWaitable.Set();
                            }

                            return;
                        }
                    }

                    if (evt is HCIEvent_GAP_End_Discoverable)
                    {
                        if (state == StateMachineStateEnum.Phase1)
                        {
                            var evtReceivedMakeDiscoverableDone = evt as HCIEvent_GAP_End_Discoverable;

                            commandWaitable.Reset();
                            evtEndDiscoverable = evtReceivedMakeDiscoverableDone;
                            eventWaitable.Set();
                        }

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_END_DISCOVERABLE
                this.SendCommand(
                    new HCICommand_GAP_End_Discoverable()
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtEndDiscoverable.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool CloseConnection(ushort connectionHandle)
        {
            HCIEvent_GAP_Link_Terminated evtLinkTerminated = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Link_Terminated)
                {
                    var evtReceivedLinkTerminated = evt as HCIEvent_GAP_Link_Terminated;

                    if (evtReceivedLinkTerminated.ConnHandle == connectionHandle)
                    {
                        commandWaitable.Reset();
                        evtLinkTerminated = evtReceivedLinkTerminated;
                        eventWaitable.Set();
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_TERMINATE_LINK_REQUEST
                this.SendCommand(
                    new HCICommand_GAP_Terminate_Link_Request(connectionHandle, HCICommand_GAP_Terminate_Link_Request.ReasonEnum.RemoteUserTerminatedConnection)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtLinkTerminated.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool AddService(HCICommand_GATT_Add_Service.UUIDEnum uuid, ushort numAttrs, out ushort startHandle, out ushort endHandle)
        {
            startHandle = 0x0000;
            endHandle = 0x0000;

            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_ADD_SERVICE
                this.SendCommand(
                    new HCICommand_GATT_Add_Service(uuid, numAttrs)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                if (evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success)
                {
                    if (evtCommandStatus.DataLen == 4)
                    {
                        startHandle = (ushort)(evtCommandStatus.CommandPayload[0] + (evtCommandStatus.CommandPayload[1] >> 8));
                        endHandle = (ushort)(evtCommandStatus.CommandPayload[2] + (evtCommandStatus.CommandPayload[3] >> 8));
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool AddAttribute(ushort uuid, HCICommand_GATT_Add_Attribute.PermissionsEnum permissions, out ushort startHandle, out ushort endHandle)
        {
            startHandle = 0x0000;
            endHandle = 0x0000;

            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_ADD_ATTRIBUTE
                this.SendCommand(
                    new HCICommand_GATT_Add_Attribute(uuid, permissions)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                if (evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success)
                {
                    if (evtCommandStatus.DataLen == 4)
                    {
                        startHandle = (ushort)(evtCommandStatus.CommandPayload[0] + (evtCommandStatus.CommandPayload[1] >> 8));
                        endHandle = (ushort)(evtCommandStatus.CommandPayload[2] + (evtCommandStatus.CommandPayload[3] >> 8));
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool AddAttribute(byte[] uuid, HCICommand_GATT_Add_Attribute.PermissionsEnum permissions, out ushort startHandle, out ushort endHandle)
        {
            startHandle = 0x0000;
            endHandle = 0x0000;

            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GAP_ADD_ATTRIBUTE
                this.SendCommand(
                    new HCICommand_GATT_Add_Attribute(uuid, permissions)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                if (evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success)
                {
                    if (evtCommandStatus.DataLen == 4)
                    {
                        startHandle = (ushort)(evtCommandStatus.CommandPayload[0] + (evtCommandStatus.CommandPayload[1] >> 8));
                        endHandle = (ushort)(evtCommandStatus.CommandPayload[2] + (evtCommandStatus.CommandPayload[3] >> 8));
                    }

                    return true;
                }
                else
                {
                    return false;
                }
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool RespondToServiceDiscoveryRequest(ushort connHandle, HCICommand_GATT_Read_By_Group_Type_Response.AttributeGroupData[] attrGroupDataArray)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_BY_GROUP_TYPE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_By_Group_Type_Response(connHandle, attrGroupDataArray)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToFindServiceAttributesByTypeRequest(ushort connHandle, HCICommand_GATT_Find_By_Type_Value_Response.HandlesInfo[] handlesInfoArray)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_BY_GROUP_TYPE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Find_By_Type_Value_Response(connHandle, handlesInfoArray)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToReadServiceAttributesByTypeRequest(ushort connHandle, HCICommand_GATT_Read_By_Type_Response.AttributeData[] attributeDataArray)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_BY_GROUP_TYPE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_By_Type_Response(connHandle, attributeDataArray)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToReadCharacteristicValueRequest(ushort connHandle, byte value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_CHAR_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_Characteristic_Value_Response(connHandle, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToReadCharacteristicValueRequest(ushort connHandle, ushort value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_CHAR_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_Characteristic_Value_Response(connHandle, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToReadCharacteristicValueRequest(ushort connHandle, bool value)
        {
            return RespondToReadCharacteristicValueRequest(connHandle, (ushort)(value ? 0x0001 : 0x0000));
        }

        public bool RespondToReadCharacteristicValueRequest(ushort connHandle, byte[] value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_CHAR_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_Characteristic_Value_Response(connHandle, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToReadCharacteristicValueRequest(ushort connHandle, string value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_CHAR_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_Characteristic_Value_Response(connHandle, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RaiseIndication(ushort connHandle, ushort attributeHandle, byte[] value, bool authenticatedLinkRequired = false)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_ATT_Handle_Value_Indication_Confirmation evtIndicationConfirmation = null;

            //this.Start();

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        if ((evt as HCIEvent_GAP_Command_Status).Status == HCIVendorSpecificEventHostErrorCode.Success)
                        {
                            state = StateMachineStateEnum.Phase1;
                        }
                        else
                        {
                            eventWaitable.Set();
                        }

                        return;
                    }
                }

                if (evt is HCIEvent_ATT_Handle_Value_Indication_Confirmation)
                {
                    if (state == StateMachineStateEnum.Phase1)
                    {
                        var evtReceivedIndicationConfirmation = evt as HCIEvent_ATT_Handle_Value_Indication_Confirmation;

                        commandWaitable.Reset();
                        evtIndicationConfirmation = evtReceivedIndicationConfirmation;
                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                //INVIO COMANDO GATT_HANDLE_VALUE_INDICATION
                this.SendCommand(
                    new HCICommand_GATT_Handle_Value_Indication(connHandle, attributeHandle, value, authenticatedLinkRequired)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtIndicationConfirmation != null && evtIndicationConfirmation.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if !(MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }
        }

        public bool RaiseIndication(ushort connHandle, ushort attributeHandle, bool value, bool authenticatedLinkRequired = false)
        {
            return RaiseIndication(connHandle, attributeHandle, new byte[] { (value ? (byte)0x01 : (byte)0x00), 0x00 }, authenticatedLinkRequired);
        }

        public bool RaiseIndication(ushort connHandle, ushort attributeHandle, byte value, bool authenticatedLinkRequired = false)
        {
            return RaiseIndication(connHandle, attributeHandle, new byte[] { value }, authenticatedLinkRequired);
        }

        public bool RaiseIndication(ushort connHandle, ushort attributeHandle, ushort value, bool authenticatedLinkRequired = false)
        {
            return RaiseIndication(connHandle, attributeHandle, new byte[] { (byte)(value & 0xFF), (byte)((value >> 8) & 0xFF) }, authenticatedLinkRequired);
        }

        public bool RaiseIndication(ushort connHandle, ushort attributeHandle, string value, bool authenticatedLinkRequired = false)
        {
            return RaiseIndication(connHandle, attributeHandle, Encoding.UTF8.GetBytes(value), authenticatedLinkRequired);
        }


        public bool RaiseNotification(ushort connHandle, ushort attributeHandle, byte[] value, bool authenticatedLinkRequired = false)
        {
            try
            {
                // INVIO COMANDO GATT_HANDLE_VALUE_NOTIFICATION
                this.SendCommand(
                    new HCICommand_GATT_Handle_Value_Notification(connHandle, attributeHandle, value, authenticatedLinkRequired)
                    );

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool RaiseNotification(ushort connHandle, ushort attributeHandle, byte value, bool authenticatedLinkRequired = false)
        {
            try
            {
                // INVIO COMANDO GATT_HANDLE_VALUE_NOTIFICATION
                this.SendCommand(
                    new HCICommand_GATT_Handle_Value_Notification(connHandle, attributeHandle, value, authenticatedLinkRequired)
                    );

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool RaiseNotification(ushort connHandle, ushort attributeHandle, ushort value, bool authenticatedLinkRequired = false)
        {
            try
            {
                // INVIO COMANDO GATT_HANDLE_VALUE_NOTIFICATION
                this.SendCommand(
                    new HCICommand_GATT_Handle_Value_Notification(connHandle, attributeHandle, value, authenticatedLinkRequired)
                    );

                return true;
            }
            catch
            {
                return false;
            }

        }

        public bool RaiseNotification(ushort connHandle, ushort attributeHandle, string value, bool authenticatedLinkRequired = false)
        {
            try
            {
                // INVIO COMANDO GATT_HANDLE_VALUE_NOTIFICATION
                this.SendCommand(
                    new HCICommand_GATT_Handle_Value_Notification(connHandle, attributeHandle, value, authenticatedLinkRequired)
                    );

                return true;
            }
            catch
            {
                return false;
            }
        }

        public bool RaiseNotification(ushort connHandle, ushort attributeHandle, bool value, bool authenticatedLinkRequired = false)
        {
            return RaiseNotification(connHandle, attributeHandle, (ushort)(value ? 0x0001 : 0x0000), authenticatedLinkRequired);
        }

        public bool RespondToReadCharacteristicBlobValueRequest(ushort connHandle, byte[] value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_CHAR_BLOB_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_Characteristic_Blob_Value_Response(connHandle, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToReadCharacteristicBlobValueRequest(ushort connHandle, string value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_READ_CHAR_BLOB_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Read_Characteristic_Blob_Value_Response(connHandle, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToWriteCharacteristicValueRequest(ushort connHandle)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_WRITE_CHAR_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Write_Characteristic_Value_Response(connHandle)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToPrepareWriteCharacteristicValueRequest(ushort connHandle, ushort attributeHandle, ushort offset, byte[] value)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_PREAPARE_WRITE_VALUE_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Prepare_Write_Characteristic_Value_Response(connHandle, attributeHandle, offset, value)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }

        public bool RespondToExchangeMTURequest(ushort connHandle, ushort mtuSize)
        {
            StateMachineStateEnum state = StateMachineStateEnum.Idle;

            HCIEvent_GAP_Command_Status evtCommandStatus = null;

            ManualResetEvent commandWaitable = new ManualResetEvent(false);
            AutoResetEvent eventWaitable = new AutoResetEvent(false);

            HCIEventHandler handler = ((evt) =>
            {
                commandWaitable.WaitOne();

                if (evt is HCIEvent_GAP_Command_Status)
                {
                    if (state == StateMachineStateEnum.Idle)
                    {
                        evtCommandStatus = evt as HCIEvent_GAP_Command_Status;

                        eventWaitable.Set();

                        return;
                    }
                }
            });

            try
            {
                this.HCIEventReceived += handler;

                // INVIO COMANDO GATT_EXCHANGE_MTU_RESPONSE
                this.SendCommand(
                    new HCICommand_GATT_Exchange_MTU_Response(connHandle, mtuSize)
                    );

                commandWaitable.Set();

                bool ok = eventWaitable.WaitOne(COMMAND_TIMEOUT_MSECS, false);

                if (!ok) return false;

                return evtCommandStatus != null && evtCommandStatus.Status == HCIBaseEntities.HCIVendorSpecificEventHostErrorCode.Success;
            }
            finally
            {
#if ! (MF_FRAMEWORK_VERSION_V4_3 || MF_FRAMEWORK_VERSION_V4_2)
                commandWaitable.Dispose();
                eventWaitable.Dispose();
#endif
                this.HCIEventReceived -= handler;
            }

        }
    }
}
